#region Imported Namespaces


using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows;
using System.Windows.Input;

using WtfManager.Framework;


#endregion


namespace WtfManager.UIManager.Commands
{
  public class CommandsService: ExtensibleService
  {
    #region Fields


    public static readonly DependencyProperty CommandBindingsProperty =
      DependencyProperty.RegisterAttached("CommandBindings", typeof(CommandBindingsCollection), typeof(CommandsService),
                                          new PropertyMetadata(CommandBindingsChanged));

    public static readonly DependencyProperty InputBindingsProperty =
      DependencyProperty.RegisterAttached("InputBindings", typeof(InputBindingsCollection), typeof(CommandsService),
                                          new PropertyMetadata(InputBindingsChanged));

    private readonly Dictionary<ICommand, ICommandHandler> _commandHandlers =
      new Dictionary<ICommand, ICommandHandler>();


    #endregion


    #region Constructors/Finalizers


    internal CommandsService()
    {
    }


    #endregion


    #region Properties/Indexers


    public override string Description
    {
      get
      {
        return "Contains handlers for UI commands.";
      }
    }


    public override string Name
    {
      get
      {
        return "Command Handlers Service";
      }
    }


    #endregion


    #region Methods


    protected override void OnStateChnaged()
    {
    }


    public static void SetCommandBindings(FrameworkElement element, CommandBindingsCollection value)
    {
      if (element == null)
      {
        throw new ArgumentNullException("element");
      }
      element.SetValue(CommandBindingsProperty, value);
    }


    public static CommandBindingsCollection GetCommandBindings(FrameworkElement element)
    {
      if (element == null)
      {
        throw new ArgumentNullException("element");
      }
      return (CommandBindingsCollection)element.GetValue(CommandBindingsProperty);
    }


    public static void SetInputBindings(FrameworkElement element, InputBindingsCollection value)
    {
      if (element == null)
      {
        throw new ArgumentNullException("element");
      }
      element.SetValue(InputBindingsProperty, value);
    }


    public static InputBindingsCollection GetInputBindings(FrameworkElement element)
    {
      if (element == null)
      {
        throw new ArgumentNullException("element");
      }
      return (InputBindingsCollection)element.GetValue(InputBindingsProperty);
    }


    private static void InputBindingsChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
    {
      if (dependencyObject is FrameworkElement && e.NewValue is InputBindingsCollection)
      {
        var frameworkElement = (FrameworkElement)dependencyObject;

        foreach (InputBinding inputBinding in (InputBindingsCollection)e.NewValue)
        {
          frameworkElement.InputBindings.Add(inputBinding);
        }
      }
    }


    private static void CommandBindingsChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
    {
      if (dependencyObject is FrameworkElement && e.NewValue is CommandBindingsCollection)
      {
        var frameworkElement = (FrameworkElement)dependencyObject;

        foreach (CommandBinding commandBinding in (CommandBindingsCollection)e.NewValue)
        {
          frameworkElement.CommandBindings.Add(commandBinding);
        }
      }
    }


    protected override FrameworkComponentState OnLoad()
    {
      return FrameworkComponentState.Loaded;
    }


    protected override void OnUnload()
    {
      _commandHandlers.Clear();
    }


    internal void CanExecuteCommand(object sender, CanExecuteRoutedEventArgs e, object parameters)
    {
      Debug.Assert(IsInitialized, "CommandsService is not yet initialized.");

      if (_commandHandlers.ContainsKey(e.Command))
      {
        ICommandHandler handler = _commandHandlers[e.Command];
        if (handler.CanExecuteCommand(sender, parameters ?? e.Parameter))
        {
          e.Handled = true;
          e.CanExecute = true;
          return;
        }
      }

      e.Handled = true;
      e.CanExecute = false;
    }


    internal void CommandExecuted(object sender, ExecutedRoutedEventArgs e, object parameters)
    {
      Debug.Assert(IsInitialized, "CommandsService is not yet initialized.");

      if (_commandHandlers.ContainsKey(e.Command))
      {
        ICommandHandler handler = _commandHandlers[e.Command];
        handler.ExecuteCommand(sender, parameters ?? e.Parameter);
      }

      e.Handled = true;
    }


    protected override void OnExtensionAdded(Service extension)
    {
      var commandHandler = (ICommandHandler)extension;
      _commandHandlers.Add(commandHandler.Command, commandHandler);
    }


    public override bool IsExtension(Service extension)
    {
      return extension is ICommandHandler;
    }


    protected override void OnExtensionRemoved(Service extension)
    {
      var commandHandler = (ICommandHandler)extension;
      _commandHandlers.Remove(commandHandler.Command);
    }


    protected override FrameworkComponentState OnInitialize()
    {
      return FrameworkComponentState.Initialized;
    }


    #endregion
  }
}